#pragma once

#include <cstring>
#include <cerrno>
#include <string>
#include <functional>
#include "Log.h"
#include "Intaddr.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

namespace zmh
{
    class serve
    {
        // 函数声明: void(const std::string &, const sockaddr_in &, int)
        typedef std::function<void(const std::string &, const Intaddr &, int)> handler;

    public:
        serve(const Intaddr &addr, const handler &func)
            : _udp_addr(addr),
              _udp_isrunning(false),
              _func(func)
        {
            // step1 --- 使用UDP协议(传输层协议、无连接、不重发、支持数据报)打开套接字
            _udp_id = socket(AF_INET, SOCK_DGRAM, 0);
            if (_udp_id == -1)
            {
                LOG(LogLevel::ERROR) << "socket mistake:" << strerror(errno);
                exit(errno);
            }
            else
                LOG(LogLevel::INFO) << "socket success";
            // step2 --- 进行IP、PORT绑定 (网络数据默认大端存储)
            if (bind(_udp_id, (const struct sockaddr *)&_udp_addr.Addr(), sizeof(_udp_addr.Addr())) == -1)
            {
                LOG(LogLevel::ERROR) << "bind mistake:" << strerror(errno);
                exit(errno);
            }
            else
                LOG(LogLevel::INFO) << "bind success";
        }
        void start()
        {
            _udp_isrunning = true;
            while (_udp_isrunning)
            {
                // 收到数据
                char buffer[1024] = {0};
                struct sockaddr_in peer;
                socklen_t peerlen = sizeof(peer);
                ssize_t n = recvfrom(_udp_id, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &peerlen);
                if (n == -1)
                {
                    LOG(LogLevel::ERROR) << "recvfrom mistake:" << strerror(errno);
                    exit(errno);
                }
                buffer[n] = 0; // 字符串结束标志
                // 处理数据
                _func(buffer, Intaddr(peer), _udp_id);
                // 记录日志
                LOG(LogLevel::INFO) << "client-" << Intaddr(peer).Name() << "-say:" << buffer;
            }
        }

    private:
        int _udp_id;         // 文件描述符
        Intaddr _udp_addr;   // socket
        bool _udp_isrunning; // 服务器的状态 --- true表运行
        handler _func;       // 回调函数 --- 服务器如何处理数据
    };

    /*
        udp协议：
        1. socket获得套接字文件描述符
        2. 将自己的socket与其进行绑定 ---
        服务器bind的IP地址为0的时候,能接受发送到该主机的所有信息,只要端口号是与我绑定的
        struct sockaddr_in addr = {0};
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(_udp_ip.c_str()); 不用bind指定的IP
        addr.sin_addr.s_addr = INADDR_ANY;
        addr.sin_port = htons(_udp_port);
        socklen_t peerlen = sizeof(peer); // 1. 标识peer的大小, 2. 标识peer读到的大小 3. 通常是相等的
    */

}
